<template>
  <div
    class="z-drag"
    ref="pageRef"
    :class="{
      'is-fixed': screen.isFull
    }"
    :style="{
      '--z-drag-zindex': zIndex,
      '--z-drag-scale': scale,
      '--z-drag-top': position.top + 'px',
      '--z-drag-left': position.left + 'px'
    }"
  >
    <div
      class="z-drag__wrap"
      @touchstart="onTouchstart"
      @touchmove.prevent="onTouchmove"
      @touchend="onTouchend"
      @touchcancel="onTouchcancel"
      @mousedown="onMousedown"
      @mousemove="onMousemove"
      @mouseup="onMouseup"
      @mouseleave="onMouseleave"
      @mousewheel="onMousewheel"
      @contextmenu.stop.prevent
    >
      <div
        class="z-drag__move"
        ref="moveRef"
        :style="{
          width: bgOpts.width + 'px',
          height: bgOpts.height + 'px'
        }"
        @click="onMoveClick"
      >
        <img
          ref="bgRef"
          class="z-drag--bg"
          :src="bgSrc"
          :style="bgStyle"
          @load="onImageLoad(bgRef)"
          @dragstart.stop.prevent
        />

        <div class="z-drag--content">
          <slot />
        </div>
      </div>
    </div>

    <div class="z-drag__operate" v-if="showOperateButton">
      <div
        circle
        class="z-drag--button z-drag--button-toggle"
        @click="operate.show = !operate.show"
      >
        <svg-icon name="drag-shezhi"></svg-icon>
      </div>

      <transition name="el-zoom-in-top">
        <div class="z-drag__operate-wrap" v-show="operate.show">
          <template v-if="isZoom">
            <div class="z-drag--button" @click="onZoom(-1)">
              <svg-icon name="drag-suoxiao"></svg-icon>
            </div>
            <div class="z-drag--button" @click="onZoom(1)">
              <svg-icon name="drag-fangda"></svg-icon>
            </div>
          </template>

          <div class="z-drag--button" v-if="!screen.isFull" @click="onFullScreen">
            <svg-icon name="drag-quanping"></svg-icon>
          </div>
          <div class="z-drag--button" v-else @click="onExitFullScreen">
            <svg-icon name="drag-shouqi"></svg-icon>
          </div>
          <div class="z-drag--button" @click="onReset">
            <svg-icon name="drag-shuaxin"></svg-icon>
          </div>
        </div>
      </transition>
    </div>

    <slot name="append" />
  </div>
</template>

<script lang="ts" setup>
import { isFullscreen, isFullscreenEnabled, setFullscreen, exitFullscreen } from '@/utils/win'
import type { Props, Pos } from './type'

const props = withDefaults(defineProps<Props>(), {
  isZoom: true,
  isMove: true,
  maxScale: 10,
  minScale: 0.1,
  // 大于菜单层级
  zIndex: 10,
  showOperateButton: true,
  operateButtonExpand: true
})

const scale = defineModel<number>({
  default: 1
})

const emits = defineEmits<{
  load: [e?: HTMLImageElement | null]
  mousedown: [e: MouseEvent | TouchEvent]
  mousemove: [e: MouseEvent | TouchEvent]
  mouseup: [e: MouseEvent | TouchEvent]
  mouseleave: [e: MouseEvent | TouchEvent]
  mousewheel: [e: WheelEvent | TouchEvent]
  'pick-dot': [e: { x: number; y: number }]
}>()

const pageRef = useTemplateRef('pageRef')

// 全屏
const screen = reactive({
  isFull: false
})

// 定位
const position = reactive({
  top: 0,
  left: 0
})

// 操作按钮
const operate = reactive({
  show: props.operateButtonExpand
})

// 图片加载
const bgRef = useTemplateRef('bgRef')
const bgOpts = reactive({
  loaded: false,
  width: 0,
  height: 0
})

// 鼠标点位
const mouseDot = reactive({
  x: 0,
  y: 0,
  temp: 0,
  start: false,
  startX: 0,
  startY: 0,
  isMove: false,

  // 双点位
  isDoubleMove: false,
  x2: 0,
  y2: 0
})

// 图片加载
const onImageLoad = (el?: HTMLImageElement | null) => {
  if (!el && (props.width == void 0 || props.height == void 0)) return
  let w = props.width ?? 0
  let h = props.height ?? 0
  if (el) {
    w = el.clientWidth
    h = el.clientHeight
  }
  bgOpts.width = w
  bgOpts.height = h
  bgOpts.loaded = true
  emits('load', el)

  onResize()
  window.addEventListener('resize', onResize, false)
}

// 窗口改变事件
const onResize = () => {
  let $page = pageRef.value
  if ($page) {
    let w = $page.clientWidth
    if (w > 0) {
      judgeInitNotScroll()
    }
  }
  // 判断是否退出全屏
  if (!isFullscreen()) {
    screen.isFull = false
  } else {
    screen.isFull = true
  }
}

// 计算初始不出现滚动条
const judgeInitNotScroll = () => {
  // 获取容器 宽高
  let $page = pageRef.value as HTMLElement
  let w = $page.clientWidth
  let h = $page.clientHeight
  // 容器比例
  let bp = w / h
  // 内容比例
  let cp = bgOpts.width / bgOpts.height

  // 默认缩放
  let s = 1
  // 判断比例大小
  // 比例大则宽大
  if (cp > bp) {
    s = w / bgOpts.width
  } else {
    s = h / bgOpts.height
  }
  scale.value = s

  calcPosition()
}

// 计算位置
const calcPosition = () => {
  // 获取容器 宽高
  let $page = pageRef.value
  let w = $page?.clientWidth || 0
  let h = $page?.clientHeight || 0

  let left = (w - bgOpts.width) / 2
  let top = (h - bgOpts.height) / 2
  position.left = left
  position.top = top
}

// 重置
const onReset = () => {
  judgeInitNotScroll()
}

// 取点
const onMoveClick = (e: MouseEvent) => {
  let x = e.offsetX
  let y = e.offsetY

  let px = Number(((x / bgOpts.width) * 100).toFixed(2))
  let py = Number(((y / bgOpts.height) * 100).toFixed(2))

  emits('pick-dot', {
    x: px,
    y: py
  })
}

// 按下
const onMousedown = (e: MouseEvent) => {
  // 判断是否可移动
  if (!props.isMove) return

  onDown(
    {
      x: e.clientX,
      y: e.clientY
    },
    e
  )
}

// 触点开始
const onTouchstart = (e: TouchEvent) => {
  // 判断是否可移动
  if (!props.isMove) return

  const dot = e.changedTouches[0]

  onDown(
    {
      x: dot.pageX,
      y: dot.pageY
    },
    e
  )
}

const onDown = (pos: Pos, e: MouseEvent | TouchEvent) => {
  // x，y
  mouseDot.x = pos.x
  mouseDot.y = pos.y

  // 暂存当前按钮坐标
  mouseDot.startX = position.left
  mouseDot.startY = position.top

  // 重置移动状态
  mouseDot.start = true
  mouseDot.isMove = false

  // 当前点击时间戳
  mouseDot.temp = new Date().getTime()
  emits('mousedown', e)
}

// 移动
const onMousemove = (e: MouseEvent) => {
  emits('mousemove', e)
  if (!mouseDot.start) return

  onMove(
    {
      x: e.clientX,
      y: e.clientY
    },
    e
  )
}

// 触点移动
const onTouchmove = (e: TouchEvent) => {
  emits('mousemove', e)
  if (!mouseDot.start) return

  const dot = e.changedTouches[0]
  if (e.changedTouches.length > 1) {
    const dot2 = e.changedTouches[1]
    if (mouseDot.isDoubleMove) {
      // 计算这次移动与上次两点之间的坐标距离
      let w1 = Math.abs(mouseDot.x - mouseDot.x2)
      let h1 = Math.abs(mouseDot.y - mouseDot.y2)
      let w2 = Math.abs(dot.pageX - dot2.pageX)
      let h2 = Math.abs(dot.pageY - dot2.pageY)
      // 坐标移动差距
      let deltax = w2 - w1
      let deltay = h2 - h1
      // 判断是否为正数
      let isPlus = deltax + deltay > 0

      // 求平方根
      let distance = Math.sqrt(deltax * deltax + deltay * deltay)
      let scale = distance / 50
      onZoom(isPlus ? scale : -scale)
    } else {
      mouseDot.isDoubleMove = true
    }
    // 记录坐标
    mouseDot.x = dot.pageX
    mouseDot.y = dot.pageY
    mouseDot.x2 = dot2.pageX
    mouseDot.y2 = dot2.pageY
    return
  }
  onMove(
    {
      x: dot.pageX,
      y: dot.pageY
    },
    e
  )
}

const onMove = (pos: Pos, _e: MouseEvent | TouchEvent) => {
  mouseDot.isMove = true

  // x，y
  const moveX = pos.x - mouseDot.x
  const moveY = pos.y - mouseDot.y

  let x = mouseDot.startX + moveX
  let y = mouseDot.startY + moveY

  position.left = x
  position.top = y
}

// 点击事件时间
const clickTemp = 200
// 弹起
const onMouseup = (e: MouseEvent) => {
  emits('mouseup', e)
  if (!mouseDot.start) return
  onUp()
}

// 触点结束
const onTouchend = (e: TouchEvent) => {
  mouseDot.isDoubleMove = false
  if (!mouseDot.start) return
  emits('mouseup', e)
  onUp()
}

const onUp = () => {
  mouseDot.start = false
  mouseDot.isMove = false

  // 当前时间戳
  let temp = new Date().getTime()
  // 计算本次滑动时间 小于 500毫秒 视为点击事件
  let calc = temp - mouseDot.temp

  // 当按下时间小于 点击设置时间 则触发点击事件
  if (calc < clickTemp && !mouseDot.isMove) {
  } else {
  }
}

// 离开
const onMouseleave = (e: MouseEvent) => {
  emits('mouseleave', e)
  onUp()
}

// 离开、中断
const onTouchcancel = (e: TouchEvent) => {
  emits('mouseleave', e)
  onUp()
}

// 滚轮
const onMousewheel = (e: WheelEvent & { wheelDeltaY: number }) => {
  emits('mousewheel', e)
  // 判断是否支持滚轮缩放
  if (!props.isZoom) return
  e.preventDefault()
  if (e.wheelDeltaY > 0) {
    onZoom(1)
  } else {
    onZoom(-1)
  }
}

// 缩放
const onZoom = (zoom: number) => {
  scale.value += zoom * 0.05
  // 判断最大或最小
  if (scale.value > props.maxScale) {
    scale.value = props.maxScale
  } else if (scale.value < props.minScale) {
    scale.value = props.minScale
  }
}

// 全屏
const onFullScreen = () => {
  screen.isFull = true
  let element = document
  // 判断是否支付 H5 api 进去全屏
  let screenEnabled = isFullscreenEnabled()

  if (screenEnabled) {
    setFullscreen(element)
  }
}

// 退出全屏
const onExitFullScreen = () => {
  screen.isFull = false
  // 判断是否支付 H5 api 进去全屏
  let screenEnabled = isFullscreenEnabled()
  if (screenEnabled) {
    let isFull = isFullscreen()
    if (isFull) {
      exitFullscreen()
    }
  }
}

const initPage = () => {
  onImageLoad()
}

onMounted(initPage)

onUnmounted(() => {
  window.removeEventListener('resize', onResize)
})

defineExpose({
  resize: onReset
})
</script>

<style lang="scss">
@use './style.scss';
</style>
